Skip to main content

Scopes 作用域的使用

Scopes - 作用域,表示一个限制范围,它最终会生成SQL查询中的where子句。它在模型定义方法sequelize.define的option参数,或通过Model.scope()方法指定。

  1. 定义

  2. 使用

  3. 合并

  4. 关联

  5. 定义

作用域允许你定义常用的查询,这样就可以很容易地在之后使用。Scopes包括所有相同属性规律的筛选器,where、where、limit等。

Scopes在定义模型时定义,可以是筛选对象,或是返回筛选对象的函数-除默认限制范围外,它只能是一个对象:

var Project = sequelize.define('project', { // Attributes }, { defaultScope: { where: { active: true } }, scopes: { deleted: { where: { deleted: true } }, activeUsers: { include: [ { model: User, where: { active: true }} ] } random: function () { return { where: { someNumber: Math.random() } } }, accessLevel: function (value) { return { where: { accessLevel: { $gte: value } } } } } });

也可以模型定义后通过addScope方法添加作用域。这对在使用include包含限制时尤其适用,因为在模型的include在定义时包括的模型可能还未定义。

默的作用总是会被使用,也就是说,在上面定义的模型中使用Project.findAll()查询时,会创建如下查询语句:

SELECT * FROM projects WHERE active = true

默认的作用域可以使用.unscoped()、.scope(null)、或引入一个其它的作用域移除:

Project.scope('deleted').findAll(); // 移除默认作用域

SELECT * FROM projects WHERE deleted = true

  1. 使用

作用域通过调用.scope方法应用到模型定义的范围内。该方法会传入一个或多个范围的名称,并范围返回一个全功能的模型实例的所有规则的方法,如:.findAll、.update、.count、destroy:

var DeletedProjects = Project.scope('deleted');

DeletedProjects.findAll(); // some time passes

// let's look for deleted projects again! DeletedProjects.findAll();

作用域被引入时有两种方式:如果不传入参数则正常引用,如果传入参数那么以对象的形式传入:

Project.scope('random', { method: ['accessLevel', 19]}).findAll();

SELECT * FROM projects WHERE someNumber = 42 AND accessLevel >= 19

  1. 合并

作用域可以同时应用多个,使用用时只要向.scope方法传入数组参数或做为连续的参数传入即可:

// 两种等价的方式 Project.scope('deleted', 'activeUsers').findAll(); Project.scope(['deleted', 'activeUsers']).findAll();

如果想在另一个范围内使用默认作用域,通过defaultScope参数传入.scope即可:

Project.scope('defaultScope', 'deleted').findAll();

SELECT * FROM projects WHERE active = true AND deleted = true

通过对象引入多重作用域时,当key值相同,后引入的作用域会覆盖前面的作用域规则:

{ scope1: { where: { firstName: 'bob', age: { $gt: 20 } }, limit: 2 }, scope2: { where: { age: { $gt: 30 } }, limit: 10 }

WHERE firstName = 'bob' AND age > 30 LIMIT 10

在这个查询中limit和age被scope2同相同的键值覆盖,

当进行查询时,默认的作用域逻辑会被同时使用:

Project.scope('deleted').findAll({ where: { firstName: 'john' } })

WHERE deleted = true AND firstName = 'john'

  1. 关联

在表关联中,Sequelize有两中不同但作用相关的概念。其区别不大,但很重要:

  • 关联作用域 - 允许你获取和设置关联定义默认属性,这在多表关联时很有用。这个作用域仅在使用get、set、add、create函数时调用

  • 模型上的作用域 - 在匹配关联时,允许你作用默认值和其它作用范围。这些作用域都适用于模型匹配,及关联查找

以下示例中包含Post 和 Comment两个模型,Comment又关联到一些其它的模型,可以理解为通过commentable_id列引用一个commentable表:

foreignKey: 'commentable_id', scope: { commentable: 'post' } });

当调用post.getComments()方法时,会自动生成WHERE commentable = 'post'语句。类似的,当发表一个新的评论Comment会自动设置为'post'。

我们还应该考虑,Post 模型应该只向用户显示状态为活跃的数据:where: { active: true }。这个活跃状态会同样应用于所关联的模型,而不是像commentable那样没有关联范围。这样,使用默认作用域直接调用Post.findAll()方法即可,同样可用于User.getPosts()方法。

禁用默认作用域,在访问器中传入scope: null即可,如:User.getPosts({ scope: null }),如下同样:

User.getPosts({ scope: ['scope1', 'scope2']});

如果你想创建一个关联作用域的快捷方法,那么可以在模型关联中定义。如,创建用户所有已删除的Post的作用域:

var Post = sequelize.define('post', attributes, { defaultScope: { where: { active: true } }, scopes: { deleted: { where: { deleted: true } } } });

User.hasMany(Post); // regular getPosts association User.hasMany(Post.scope('deleted'), { as: 'deletedPosts' });

User.getPosts(); // WHERE active = true User.getDeletedPosts(); // WHERE deleted = true